﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="NestedAsyncHelper.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved. 
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Threading;

namespace Microsoft.WindowsAzure.StorageClientExtensions
{
    /// <summary>
    /// This class helps to build nested Azynchrnonous pattern calls    
    /// </summary>
    public class NestedAsyncHelper
    {
        /// <summary>
        /// Wrapper class allows to override AsyncState of existing IAsyncResult instance with user-provided object.
        /// </summary>
        private class AsyncResultWrapper : IAsyncResult
        {
            private readonly IAsyncResult _parent;
            private readonly Object _overrideState;

            public AsyncResultWrapper(IAsyncResult parent, object overrideState)
            {
                _parent = parent;
                _overrideState = overrideState;
            }

            public object AsyncState
            {
                get { return _overrideState; }
            }

            public IAsyncResult OriginalAsyncResult
            {
                get { return _parent; }
            }

            public WaitHandle AsyncWaitHandle
            {
                get { return _parent.AsyncWaitHandle; }
            }

            public bool CompletedSynchronously
            {
                get { return _parent.CompletedSynchronously; }
            }

            public bool IsCompleted
            {
                get { return _parent.IsCompleted; }
            }
        }

        private Object _extraState;        
        private Object _state;
        private AsyncCallback _callback;
        
        public static NestedAsyncHelper WrapBeginParameters(AsyncCallback callback, object state, object extraState)
        {
            // wrap all the state we need to pass to the EndAddMessage
            return new NestedAsyncHelper() { _state = state, _callback = callback, _extraState = extraState };
        }

        public static void Callback(IAsyncResult asyncResult)
        {
            // unwrap the original state and original callback
            NestedAsyncHelper myState = (NestedAsyncHelper)asyncResult.AsyncState;

            // call the original callback with substituted async result
            if (myState != null && myState._callback != null)
            {
                myState._callback(new AsyncResultWrapper(asyncResult, myState._state));
            }
        }

        public IAsyncResult WrapAsyncResult(IAsyncResult asyncResult)
        {
            return new AsyncResultWrapper(asyncResult, _state);
        }

        public static object GetExtraState(IAsyncResult asyncResult)
        {
            AsyncResultWrapper asyncWrapper = (AsyncResultWrapper)asyncResult;

            // original Async state is a wrapper
            NestedAsyncHelper myState = (NestedAsyncHelper)asyncWrapper.OriginalAsyncResult.AsyncState;

            return myState._extraState;
        }

        public static IAsyncResult UnwrapAsyncResult(IAsyncResult asyncResult)
        {
            AsyncResultWrapper asyncWrapper = (AsyncResultWrapper)asyncResult;
            return asyncWrapper.OriginalAsyncResult;
        }

    }
}
